package main

import (
	"go/ast"
	"go/parser"
	"go/token"
	"path"
	"strings"

	"github.com/fatih/structtag"
)

type Parser struct {
	source *Source
}

func NewParser(source *Source) *Parser {
	return &Parser{source: source}
}

func (p *Parser) Parse() error {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset,
		path.Join(p.source.Filedir, p.source.Filename), nil, 0)
	if err != nil {
		panic(err)
	}

	p.source.Package = file.Name.Name

	for i := range file.Decls {
		gdecl, ok := file.Decls[i].(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, spec := range gdecl.Specs {
			if tspec, ok := spec.(*ast.TypeSpec); ok {
				switch typ := tspec.Type.(type) {
				case *ast.StructType:
					p.parseFieldList(tspec.Name.Name, typ.Fields)
				}
			}
		}
	}

	return nil
}

func (p *Parser) parseFieldList(name string, fields *ast.FieldList) {
	struc := &Struct{
		Name:   name,
		Field:  make(map[string]*StructField),
		Fields: make([]*StructField, 0),
	}

	for _, field := range fields.List {
		f := &StructField{
			Name: field.Names[0].Name,
			Type: field.Type.(*ast.Ident).Name,
		}

		if field.Tag != nil {
			tags, err := structtag.Parse(field.Tag.Value)
			if err == nil {
				for _, tag := range tags.Tags() {
					f.Tags = append(f.Tags, &StructTag{
						Key:     tag.Key,
						Name:    tag.Name,
						Options: tag.Options,
					})
				}
			}
			f.TagRaw = strings.Trim(field.Tag.Value, "`")
		}

		struc.Field[f.Name] = f
		struc.Fields = append(struc.Fields, f)
	}

	p.source.Structs = append(p.source.Structs, struc)
}
